/*---------------------------------------------------------------------
 * File name: hal_timer.h
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#pragma once
/*
*/
#include <cstdint>
#include <cstring>
/*
*/
#define HTIM_PORT_BITS   5     
#define HTIM_CTM_BITS    3     //Counter mode
#define HTIM_INT_BITS    1     
#define HTIM_FREQ_BITS   24  
#define HTIM_CLOCK_BITS  30 
/*
*/
#define HTIM_PORT_POS    (0)
#define HTIM_CTM_POS     (HTIM_PORT_POS + HTIM_PORT_BITS)
#define HTIM_INT_POS     (HTIM_CTM_POS + HTIM_CTM_BITS)
#define HTIM_FREQ_POS    (HTIM_INT_POS + HTIM_INT_BITS)
#define HTIM_CLOCK_POS   (HTIM_FREQ_POS + HTIM_FREQ_BITS)
/*
*/
#define HTIM_PORT_MASK     ((1<<HTIM_PORT_BITS) - 1)
#define HTIM_CTM_MASK      ((1<<HTIM_CTM_BITS) - 1)
#define HTIM_INT_MASK      ((1<<HTIM_INT_BITS) - 1)
#define HTIM_FREQ_MASK     ((1ull<<HTIM_FREQ_BITS) - 1)
#define HTIM_CLOCK_MASK    ((1ull<<HTIM_CLOCK_BITS) - 1)
#define HTIM_CFG_MASK      (((uint64_t)HTIM_CTM_MASK << HTIM_CTM_POS) |\
                            ((uint64_t)HTIM_INT_MASK << HTIM_INT_POS) |\
                            ((uint64_t)HTIM_FREQ_MASK << HTIM_FREQ_POS) |\
                            ((uint64_t)HTIM_CLOCK_MASK << HTIM_CLOCK_POS))
/*
*/
#define HTIM_0   (1 << HTIM_PORT_POS)
#define HTIM_1   (2 << HTIM_PORT_POS)
#define HTIM_2   (3 << HTIM_PORT_POS)
#define HTIM_3   (4 << HTIM_PORT_POS)
#define HTIM_4   (5 << HTIM_PORT_POS)
#define HTIM_5   (6 << HTIM_PORT_POS)
#define HTIM_6   (7 << HTIM_PORT_POS)
#define HTIM_7   (8 << HTIM_PORT_POS)
#define HTIM_8   (9 << HTIM_PORT_POS)
#define HTIM_9   (10 << HTIM_PORT_POS)
#define HTIM_10  (11 << HTIM_PORT_POS)
#define HTIM_11  (12 << HTIM_PORT_POS)
#define HTIM_12  (13 << HTIM_PORT_POS)
#define HTIM_13  (14 << HTIM_PORT_POS)
#define HTIM_14  (15 << HTIM_PORT_POS)
#define HTIM_15  (16 << HTIM_PORT_POS)
#define HTIM_16  (17 << HTIM_PORT_POS)
#define HTIM_17  (18 << HTIM_PORT_POS)
#define HTIM_18  (19 << HTIM_PORT_POS)
#define HTIM_19  (20 << HTIM_PORT_POS)
#define HTIM_20  (21 << HTIM_PORT_POS)
#define HTIM_PORT(param)  (HTIM_##param >> HTIM_PORT_POS)
/*
*/
#define HTIM_CTM_UP      (0 << HTIM_CTM_POS)  
#define HTIM_CTM_DOWN    (1 << HTIM_CTM_POS) 
#define HTIM_CTM_CA1     (2 << HTIM_CTM_POS)
#define HTIM_CTM_CA2     (3 << HTIM_CTM_POS)
#define HTIM_CTM_CA3     (4 << HTIM_CTM_POS)
#define HTIM_CTM(param)  (HTIM_CTM_##param >> HTIM_CTM_POS)
/*
Just control update interrupt
*/
#define HTIM_UP_INT_DISABLE   (0 << HTIM_INT_POS)  
#define HTIM_UP_INT_ENABLE    (1 << HTIM_INT_POS) 
#define HTIM_INT(param)    (HTIM_UP_INT_##param >> HTIM_INT_POS)
/*
*/
#define HTIM_FREQ(freq)    ((uint64_t)freq << HTIM_FREQ_POS)
#define HTIM_CLOCK(clock)  ((uint64_t)clock << HTIM_CLOCK_POS)
/*
*/
#define HTIM_CFG_EXTRACT(cfg, name)  ((cfg >> HTIM_##name##_POS) & HTIM_##name##_MASK)
/*
*/
class HAL_Timer
{
public:
  HAL_Timer():
    port(0),
    cfg(0),
    clock(0),
    freq(0),
    period(0xffffffff),
    _index(0),
    _counter_bits(16),
    _counter_dir(1),
    _update_counter(nullptr)
  {}
  
  uint32_t port;
  uint64_t cfg;
  uint32_t clock;
  uint32_t freq;
  uint32_t period;
  
  bool     init(uint64_t cfg0);
  bool     config(uint64_t cfg0);  //Just config the param with CFG_MASK
  uint64_t ticks();
  uint64_t micros();   //Can only used in dir=1 mode
  uint64_t millis() {  //Can only used in dir=1 mode
    return micros() / 1000;
  }
  void     attach_irq(void(*fun)(void *param, uint32_t status), void *param);
  void     detach_irq() {
              attach_irq(nullptr, nullptr);
           }
  
  void delay_us(uint32_t dt) {  //Can only used in dir=1 mode
    uint64_t t0;
    t0 = micros();
    while((uint32_t)(micros() - t0) <= dt);
  }
  
  void delay_ms(uint32_t dt) {  //Can only used in dir=1 mode
    uint64_t t0;
    t0 = millis();
    while((uint32_t)(millis() - t0) <= dt);
  }
  
private:
  uint8_t _index;
  uint8_t _counter_bits;
  int8_t  _counter_dir;    //-1(down) 0(center aligned) 1(up)
  uint64_t *_update_counter;
};




